#include "p2p_call_agent.hpp"
#include "api/p2pms_common.hpp"
#include "p2pms_log.hpp"
#include "p2pms_util.hpp"
#include "capturer_track_source.hpp"

#include <functional>
#include <thread>
#include <chrono>

#include "api/stats/rtcstats_objects.h"

#include "api/video/video_sink_interface.h"
#include "modules/video_capture/video_capture_factory.h"
#include "api/video/video_frame.h"
#include "api/video/video_source_interface.h"
#include "pc/video_track_source.h"
#include "modules/video_capture/video_capture.h"
#include "modules/video_capture/video_capture_defines.h"
#include "modules/video_capture/video_capture_impl.h"

using namespace std::placeholders;

const char kCandidateSdpMidName[] = "sdp_mid";
const char kCandidateSdpMlineIndexName[] = "sdp_mline_index";
const char kCandidateSdpName[] = "candidate";


namespace p2pms
{

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
P2PCallAgent::P2PCallAgent(CallRole role, const std::string& user_id, IPcFactoryPtr factory, 
	ICallHandler* handler, rtc::Thread* sdk_thread, std::shared_ptr<IMsgDispatcher> dispatcher)
	: BaseCallAgent(CallMode::CALL_MODE_P2P, role, user_id)
	, m_call_handler(handler)
	, m_pc_factory(factory)
	, m_sdk_thread(sdk_thread)
	, m_dispatcher(dispatcher)
{
	assert(m_call_handler);
	assert(m_pc_factory);
	assert(m_sdk_thread);
	assert(m_dispatcher);

	LOG_INF("P2PCallAgent created");
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
P2PCallAgent::~P2PCallAgent()
{
	LOG_INF("Destroying P2PCallAgent");

	if (m_pc_wrapper) {
		m_pc_wrapper->Destroy();
	}

	UnregisterMsgHandlers();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::Init()
{
	RegisterMsgHandlers();

	LOG_INF("P2PCallAgent initialized");
	
	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::MakeCall(const std::string& callee_id)
{
	if (callee_id.empty()) {
		LOG_ERR("Callee ID is empty");
		return ERR_INVALID_PARAM;
	}

	if (m_call_role != CallRole::Caller) {
		LOG_ERR("Call role is not Caller, cannot initiate call");
		return ERR_FAILED;
	}

	if (!m_dispatcher->SendMsg(MakeCallMakeReqMsg("p2p", GetUserId(), callee_id), CALL_MAKE_RES)) {
		LOG_ERR("Failed to send call request message");
		return ERR_FAILED;
	}

	m_callee_id = callee_id;

	CallStateChanged(CallState::Calling); // TODO: Call ID is empty now!!!

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::IncomingCall(const CallMakeNotifyData& notify_data)
{
	LOG_INF("Incoming call: {}", nlohmann::json(notify_data).dump());

	if (notify_data.call_type != "p2p") {
		LOG_ERR("Unsupported call type: {}", notify_data.call_type);
		return ERR_FAILED;
	}

	if (notify_data.call_id.empty()) {
		LOG_ERR("Call ID is empty in call notification");
		return ERR_FAILED;
	}

	if (notify_data.caller_id.empty()) {
		LOG_ERR("Caller ID is empty in call notification");
		return ERR_FAILED;
	}

	if (notify_data.callee_id != m_callee_id) {
		LOG_ERR("Callee ID in notification does not match agent's callee ID: {} != {}", 
			notify_data.callee_id, m_callee_id);
		return ERR_FAILED;
	}

	if (m_call_role != CallRole::Callee) {
		LOG_ERR("Call role is not Callee, cannot handle incoming call");
		return ERR_FAILED;
	}

	m_call_id = notify_data.call_id;
	m_caller_id = notify_data.caller_id;

	CallStateChanged(CallState::Connected);

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::TerminateCall()
{
	LOG_INF("Terminating call: {}", m_call_id);

	if (m_call_state == CallState::Idle ||
		m_call_state == CallState::Terminating ||
		m_call_state == CallState::Terminated) {
		LOG_WRN("Invalid call state:{} to terminate call, no action taken", m_call_state);
		return;
	}

	auto result = m_dispatcher->SendMsgSync(MakeCallEndReqMsg(m_call_id, GetUserId()), CALL_END_RES);
	if (result.cbt == CBType::Timeout) {
		LOG_ERR("Failed to terminate call: Timeout");
	} else if (result.cbt == CBType::Error) {
		LOG_ERR("Failed to send terminate call request");
	} else {
		int code = 0;
		CallEndResData resData;
		if (!ParseResponse(result.msg, resData, code)) {
			LOG_ERR("Parse call end response failed");
		}
		else {
			if (code == 0) {
				LOG_INF("Call terminated successfully");
			}
			else {
				LOG_WRN("Call end response error: {}", code);
			}
		}
	}

	CallStateChanged(CallState::Terminated);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::StartPublish(const MediaInfo& media)
{
	LOG_INF("Starting to publish media: {}", ToString(media));

	if (m_call_state != CallState::Connected) {
		LOG_ERR("Cannot publish media, call is not connected");
		return ERR_FAILED;
	}

	if (m_pub_medias.find(media.media_id) != m_pub_medias.end()) {
		LOG_ERR("Media with ID {} is already being published", media.media_id);
		return ERR_FAILED;
	}

	if (!m_dispatcher->SendMsg(MakeP2PStartPublishReqMsg(m_call_id, GetUserId(), media),
		P2P_START_PUBLISH_RES)) {
		LOG_ERR("Failed to send start publish media request");
		return ERR_FAILED;
	}

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::StopPublish(const MediaInfo& media)
{
	LOG_INF("Stopping to publish media: {}", ToString(media));

	if (m_call_state != CallState::Connected) {
		LOG_ERR("Cannot stop publish media, call is not connected");
		return ERR_FAILED;
	}

	auto iter = m_pub_medias.find(media.media_id);
	if (iter == m_pub_medias.end()) {
		LOG_ERR("Media with ID {} is not being published", media.media_id);
		return ERR_FAILED;
	}

	// 发送停止发布请求
	if (!m_dispatcher->SendMsg(MakeP2PStopPublishReqMsg(m_call_id, GetUserId(), media), 
		P2P_STOP_PUBLISH_RES)) {
		LOG_ERR("Failed to send stop publish media request");
		return ERR_FAILED;
	}

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::StartSubscribe(const std::string& user_id, const MediaInfo& media)
{
	LOG_INF("Starting to subscribe media from user: {}, media: {}", user_id, ToString(media));

	if (m_call_state != CallState::Connected) {
		LOG_ERR("Cannot subscribe media, call is not connected");
		return ERR_FAILED;
	}

	if (!m_dispatcher->SendMsg(MakeP2PStartSubscribeReqMsg(m_call_id, GetUserId(), user_id, media),
		P2P_START_SUBSCRIBE_RES)) {
		LOG_ERR("Failed to send start subscribe media request");
		return ERR_FAILED;
	}

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PCallAgent::StopSubscribe(const std::string& user_id, const MediaInfo& media)
{
	LOG_INF("Stopping to subscribe media from user: {}, media: {}", user_id, ToString(media));

	if (m_call_state != CallState::Connected) {
		LOG_ERR("Cannot stop subscribe media, call is not connected");
		return ERR_FAILED;
	}

	if (m_sub_medias.find(media.media_id) == m_sub_medias.end()) {
		LOG_ERR("Media with ID {} is not being subscribed", media.media_id);
		return ERR_FAILED;
	}

	if (!m_dispatcher->SendMsg(MakeP2PStopSubscribeReqMsg(m_call_id, GetUserId(), user_id, media), 
		P2P_STOP_SUBSCRIBE_RES)) {
		LOG_ERR("Failed to send stop subscribe media request");
		return ERR_FAILED;
	}
	
	return ERR_OK;
}

//#include "api/stats/rtc_stats.h"


class StatsCallback : public webrtc::RTCStatsCollectorCallback
{
public:
	void OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override
	{
		auto inbound_stats = report->GetStatsOfType<webrtc::RTCInboundRtpStreamStats>();
		for (const auto* stat : inbound_stats) {
			LOG_INF("bytes_received: {}", stat->bytes_received.ValueToString());
		}

		auto outbound_stats = report->GetStatsOfType<webrtc::RTCOutboundRtpStreamStats>();
		for (const auto* stat : outbound_stats) {
			LOG_INF("bytes_sent: {}", stat->bytes_sent.ValueToString());
		}
	}
};

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnTimer()
{
	/*if (!m_peer_connection) {
		LOG_ERR("PeerConnection is not initialized, cannot get stats");
		return;
	}

	m_peer_connection->GetStats(new rtc::RefCountedObject<StatsCallback>());*/
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::RegisterMsgHandlers()
{
	m_dispatcher->Register(CALL_MAKE_RES,
		[this](const Message& msg, CBType cbt) { OnCallMakeRes(msg, cbt); });
	m_dispatcher->Register(P2P_CANDIDATE_NOTIFY,
		[this](const Message& msg, CBType cbt) { OnCandidateNotify(msg, cbt); });
	m_dispatcher->Register(P2P_NEGOTIATE_REQ,
		[this](const Message& msg, CBType cbt) { OnNegotiateReq(msg, cbt); });
	m_dispatcher->Register(P2P_NEGOTIATE_RES,
		[this](const Message& msg, CBType cbt) { OnNegotiateRes(msg, cbt); });
	m_dispatcher->Register(CALL_END_NOTIFY,
		[this](const Message& msg, CBType cbt) { OnCallEndNotify(msg, cbt); });
	m_dispatcher->Register(CALL_END_RES,
		[this](const Message& msg, CBType cbt) { OnCallEndRes(msg, cbt); });
	m_dispatcher->Register(P2P_START_PUBLISH_NOTIFY,
		[this](const Message& msg, CBType cbt) { OnStartPublishNotify(msg, cbt); });
	m_dispatcher->Register(P2P_START_PUBLISH_RES,
		[this](const Message& msg, CBType cbt) { OnStartPublishRes(msg, cbt); });
	m_dispatcher->Register(P2P_START_SUBSCRIBE_RES,
		[this](const Message& msg, CBType cbt) { OnStartSubscribeRes(msg, cbt); });
	m_dispatcher->Register(P2P_STOP_SUBSCRIBE_RES,
		[this](const Message& msg, CBType cbt) { OnStopSubscribeRes(msg, cbt); });
	m_dispatcher->Register(P2P_START_PRODUCE_NOTIFY,
		[this](const Message& msg, CBType cbt) { OnStartProduceNotify(msg, cbt); });
	m_dispatcher->Register(P2P_STOP_PUBLISH_RES,
		[this](const Message& msg, CBType cbt) { OnStopPublishRes(msg, cbt); });
	m_dispatcher->Register(P2P_STOP_PUBLISH_NOTIFY,
		[this](const Message& msg, CBType cbt) { OnStopPublishNotify(msg, cbt); });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::UnregisterMsgHandlers()
{
	m_dispatcher->Unregister(CALL_MAKE_RES);
	m_dispatcher->Unregister(P2P_CANDIDATE_NOTIFY);
	m_dispatcher->Unregister(P2P_NEGOTIATE_REQ);
	m_dispatcher->Unregister(P2P_NEGOTIATE_RES);
	m_dispatcher->Unregister(CALL_END_NOTIFY);
	m_dispatcher->Unregister(CALL_END_RES);
	m_dispatcher->Unregister(P2P_START_PUBLISH_NOTIFY);
	m_dispatcher->Unregister(P2P_START_PUBLISH_RES);
	m_dispatcher->Unregister(P2P_START_SUBSCRIBE_RES);
	m_dispatcher->Unregister(P2P_STOP_SUBSCRIBE_RES);
	m_dispatcher->Unregister(P2P_START_PRODUCE_NOTIFY);
	m_dispatcher->Unregister(P2P_STOP_PUBLISH_RES);
	m_dispatcher->Unregister(P2P_STOP_PUBLISH_NOTIFY);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::PostTaskToSdkThread(std::function<void()> task)
{
	if (m_sdk_thread && task) {
		m_sdk_thread->PostTask(std::move(task));
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::CallStateChanged(CallState state)
{
	if (m_call_state != state) {
		m_call_state = state;
		if (m_call_handler) {
			m_call_handler->OnCallStateChanged(m_call_id, state);
		}
	}
	else {
		LOG_WRN("Call state unchanged: {}", ToString(state));
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::SendSdpToServer(const std::string& rid, webrtc::SdpType type, const std::string& sdp)
{
	if (type == webrtc::SdpType::kOffer) {
		LOG_INF("Sending offer: \n{}", sdp);
		if (!m_dispatcher->SendMsg(
			MakeP2PNegotiateReqMsg(m_call_id, GetUserId(), sdp), P2P_NEGOTIATE_RES)) {
			LOG_ERR("Failed to send negotiate request message");
		}
	}
	else if (type == webrtc::SdpType::kAnswer) {
		LOG_INF("Sending answer: \n{}", sdp);
		if (!m_dispatcher->SendMsg(
			MakeP2PNegotiateResMsg(rid, 0, "success", m_call_id, GetUserId(), sdp))) {
			LOG_ERR("Failed to send negotiate response message");
		}
	}
	else {
		LOG_ERR("Unknown SDP type");
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::StartNegotiationAsOfferer()
{
	if (m_is_negotiating) {
		LOG_WRN("Negotiation already in progress");
		return;
	}

	if (!m_pc_wrapper) {
		if (!CreatePeerConnection()) {
			LOG_ERR("Failed to create PeerConnection for negotiation");
			return;
		}
		LOG_INF("Created PeerConnection for negotiation");

		if (!CreateDefaultTransceivers()) {
			LOG_ERR("Failed to create default transceivers");
			m_pc_wrapper = nullptr;
			return;
		}
	}

	LOG_INF("Starting negotiation as offerer...");

	m_is_negotiating = true;
	m_neg_state = NegotiationState::kCreatingOffer;

	try {
		// 同步等待 CreateOffer 完成
		auto offer = m_pc_wrapper->CreateOffer().get();

		LOG_INF("Local description set, sending offer...");
		SendSdpToServer(GenerateRandomStr(), webrtc::SdpType::kOffer, offer);
		m_neg_state = NegotiationState::kWaitingForAnswer;
	} 
	catch (const std::exception& e) {
		LOG_ERR("Negotiation failed during offer creation: {}", e.what());
		m_neg_state = NegotiationState::kIdle;
		m_is_negotiating = false;
		// TODO: 通知上层协商失败
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::ApplyPendingRemoteCandidates()
{
	if (m_pending_remote_candidates.empty()) {
		LOG_INF("No pending candidates to apply.");
		return;
	}

	for (auto& candidate : m_pending_remote_candidates) {
		m_pc_wrapper->AddIceCandidate(std::move(candidate));
	}
	m_pending_remote_candidates.clear();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::HandleRemoteAnswer(const std::string& sdp)
{
	if (m_neg_state != NegotiationState::kWaitingForAnswer) {
		LOG_WRN("Received answer in unexpected state: {}", static_cast<int>(m_neg_state));
		return;
	}

	LOG_INF("Handling remote answer...");

	webrtc::SdpParseError error;
	auto answer_desc = webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp, &error);
	if (!answer_desc) {
		LOG_ERR("Failed to parse remote answer SDP: {}", error.description);
		return;
	}

	try {
		// 同步等待 SetRemoteDescription 完成
		m_pc_wrapper->SetRemoteDescription(answer_desc.release()).get();

		LOG_INF("Remote description (answer) set. Negotiation stable.");
		m_neg_state = NegotiationState::kStable;
		m_is_negotiating = false;

		ApplyPendingRemoteCandidates(); // 应用之前缓存的候选者
	} catch (const std::exception& e) {
		LOG_ERR("Failed to set remote answer: {}", e.what());
		m_neg_state = NegotiationState::kIdle;
		// TODO: 通知上层协商失败
		m_is_negotiating = false;
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::HandleRemoteOffer(const std::string& rid, const std::string& sdp)
{
	if (m_neg_state != NegotiationState::kIdle && m_neg_state != NegotiationState::kStable) {
		LOG_WRN("Received offer in unexpected state: {}", static_cast<int>(m_neg_state));
		// TODO: 实现 "glare" (冲突) 处理
		return;
	}
	if (!m_pc_wrapper) {
		if (!CreatePeerConnection()) {
			LOG_ERR("Failed to create PeerConnection for incoming offer");
			return;
		}
	}

	LOG_INF("Handling remote offer...");
	m_neg_state = NegotiationState::kProcessingOffer;

	webrtc::SdpParseError error;
	std::unique_ptr<webrtc::SessionDescriptionInterface> offer_desc =
		webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp, &error);
	if (!offer_desc) {
		LOG_ERR("Failed to parse remote offer SDP: {}", error.description);
		m_neg_state = NegotiationState::kIdle;
		return;
	}

	try {
		// 同步等待 SetRemoteDescription 完成
		m_pc_wrapper->SetRemoteDescription(offer_desc.release()).get();

		// 同步等待 CreateAnswer 完成
		auto answer = m_pc_wrapper->CreateAnswer().get();

		LOG_INF("Local description (answer) set, sending answer...");
		SendSdpToServer(rid, webrtc::SdpType::kAnswer, answer);
		m_neg_state = NegotiationState::kStable;

		ApplyPendingRemoteCandidates();
	} catch (const std::exception& e) {
		LOG_ERR("Negotiation failed during answer creation: {}", e.what());
		m_neg_state = NegotiationState::kIdle;
		// TODO: 通知上层协商失败
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnCallMakeRes(const Message& msg, CBType cbt)
{
	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Call make response timeout");
			CallStateChanged(CallState::Terminated);
			return;
		}

		int code;
		CallMakeResData res_data;
		if (!ParseResponse(msg, res_data, code)) {
			LOG_ERR("Failed to parse call response message");
			return;
		}

		if (code != 0) {
			LOG_ERR("Call response error");
			CallStateChanged(CallState::Terminated);
			return;
		}

		if (res_data.call_id.empty()) {
			LOG_ERR("Call response missing call ID");
			return;
		}

		m_call_id = res_data.call_id;

		CallStateChanged(CallState::Connected);

		StartNegotiationAsOfferer(); // 呼叫成功，接下来进行 PeerConnection 的创建和 SDP 的协商
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnCandidateNotify(const Message& msg, CBType cbt)
{
	LOG_INF("Received candidate notify message");

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt != CBType::Normal) {
			LOG_ERR("Received non-normal message type: {}", cbt);
			return;
		}

		if (!msg.data.is_object()) {
			LOG_ERR("Candidate notify data is not an object");
			return;
		}
		nlohmann::json jmessage = msg.data;

		// Parse the candidate information from the JSON object

		std::string sdp_mid;
		int sdp_mlineindex = 0;
		std::string sdp;
		try {
			sdp_mid = jmessage.at(kCandidateSdpMidName).get<std::string>();
			sdp_mlineindex = jmessage.at(kCandidateSdpMlineIndexName).get<int>();
			sdp = jmessage.at(kCandidateSdpName).get<std::string>();
		} catch (const nlohmann::json::exception& e) {
			LOG_ERR("Failed to parse candidate JSON: {}", e.what());
			return;
		}

		webrtc::SdpParseError error;
		std::unique_ptr<webrtc::IceCandidateInterface> candidate(
			webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error));
		if (!candidate.get()) {
			LOG_ERR("Can't parse received candidate message. SdpParseError was: {}", error.description);
			return;
		}

		if (m_neg_state == NegotiationState::kWaitingForAnswer || 
			m_neg_state == NegotiationState::kStable) {
			m_pc_wrapper->AddIceCandidate(std::move(candidate));
		} else {
			m_pending_remote_candidates.push_back(std::move(candidate));
			LOG_INF("Added candidate to pending list, waiting for negotiation to complete");
		}
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PCallAgent::ParseNegotiateData(const nlohmann::json& data, P2PNegotiateReqData& nrd)
{
	try {
		nrd = data.get<P2PNegotiateReqData>();
	}
	catch (const std::exception& e) {
		LOG_ERR("Failed to parse negotiate request data: {}", e.what());
		return false;
	}

	if (nrd.call_id.empty()) {
		LOG_ERR("Negotiate request missing call ID");
		return false;
	}

	if (nrd.user_id.empty()) {
		LOG_ERR("Negotiate request missing user ID");
		return false;
	}

	if (nrd.sdp.empty()) {
		LOG_ERR("Negotiate request missing SDP data");
		return false;
	}

	if (nrd.call_id != m_call_id) {
		LOG_ERR("Negotiate request call ID does not match current call ID: {} != {}",
			nrd.call_id, m_call_id);
		return false;
	}

	return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnNegotiateReq(const Message& msg, CBType cbt)
{
	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt != CBType::Normal) {
			LOG_ERR("Received non-normal message type: {}", cbt);
			return;
		}

		if (m_call_role == CallRole::Undefined) {
			LOG_ERR("Call role is undefined, cannot process negotiate request");
			return;
		}

		if (msg.data.is_null()) {
			LOG_ERR("Negotiate request data is null");
			return;
		}

		P2PNegotiateReqData negotiate_req_data;
		if (!ParseNegotiateData(msg.data, negotiate_req_data)) {
			LOG_ERR("Parse negotiate data failed");
			return;
		}

		HandleRemoteOffer(msg.rid, negotiate_req_data.sdp);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnNegotiateRes(const Message& msg, CBType cbt)
{
	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Negotiate response timeout");
			return;
		}

		int code = 0;
		P2PNegotiateResData res_data;
		if (!ParseResponse(msg, res_data, code)) {
			LOG_ERR("Failed to parse negotiate response message");
			return;
		}

		if (code != 0) {
			LOG_ERR("Negotiate response error: {}", code);
			return;
		}

		HandleRemoteAnswer(res_data.sdp);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnCallEndNotify(const Message& msg, CBType cbt)
{
	assert(m_call_role != CallRole::Undefined);

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg]() {
		CallEndNotifyData notify_data;
		if (!ParseRequest(msg, notify_data)) {
			return;
		}

		if (notify_data.call_id != m_call_id) {
			LOG_ERR("Call end notify call ID does not match current call ID: {} != {}",
				notify_data.call_id, m_call_id);
			return;
		}

		m_dispatcher->SendMsg(MakeCallEndAckMsg(
			notify_data.call_id, 0, "ok", notify_data.call_id, notify_data.user_id));

		CallStateChanged(CallState::Terminated);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnCallEndRes(const Message& msg, CBType cbt)
{
	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Call end response timeout");
		}
		else {
			int code = 0;
			CallEndResData res_data;

			if (!ParseResponse(msg, res_data, code)) {
				LOG_ERR("Failed to parse call end response message");
				return;
			}

			if (code != 0) {
				LOG_ERR("Call end response error: {}", code);
			}

			LOG_INF("Call end success");
		}

		CallStateChanged(CallState::Terminated);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStartPublishNotify(const Message& msg, CBType cbt)
{
	LOG_INF("Received start publish media notify");

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt != CBType::Normal) {
			LOG_ERR("Received non-normal message type: {}", cbt);
			return;
		}

		if (msg.data.is_null()) {
			LOG_ERR("Start publish media notify data is null");
			return;
		}
		P2PStartPublishNotifyData notify_data;
		try {
			notify_data = msg.data.get<P2PStartPublishNotifyData>();
		} catch (const std::exception& e) {
			LOG_ERR("Failed to parse start publish media notify data: {}", e.what());
			return;
		}

		if (notify_data.call_id != m_call_id) {
			LOG_ERR("Start publish media notify call ID does not match current call ID: {} != {}",
				notify_data.call_id, m_call_id);
			return;
		}

		if (notify_data.user_id.empty()) {
			LOG_ERR("Start publish media notify missing user ID");
			return;
		}

		if (m_sub_medias.find(notify_data.media.media_id) != m_sub_medias.end()) {
			LOG_ERR("Media with ID {} is already being published by user {}", 
				notify_data.media.media_id, notify_data.user_id);
			return;
		}

		m_dispatcher->SendMsg(MakeCommonResMsg(P2P_START_PUBLISH_ACK, msg.rid, 0,
			"Start publish media notify received"));

		m_sub_medias.insert(
			{ notify_data.media.media_id, {notify_data.user_id, notify_data.media} });

		assert(m_call_handler);
		m_call_handler->OnStartPublish(notify_data.user_id, notify_data.media);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStartPublishRes(const Message& msg, CBType cbt)
{
	LOG_INF("Received start publish media response");

	assert(m_sdk_thread);
	assert(m_call_handler);

	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Stop publish timeout", cbt);
			P2PStartPublishReqData req_data;
			if (ParseRequest(msg, req_data)) {
				m_call_handler->OnStartPublishResult(req_data.media, false);
			}
			return;
		}

		int code;
		P2PStopPublishResData res_data;
		if (!ParseResponse(msg, res_data, code)) {
			return;
		}

		if (code != 0) {
			LOG_ERR("Stop publish response error");
			m_call_handler->OnStartPublishResult(res_data.media, false);
			return;
		}

		m_pub_medias.insert({ res_data.media.media_id, {res_data.media, nullptr, nullptr} });

		m_call_handler->OnStartPublishResult(res_data.media, true);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStopPublishRes(const Message& msg, CBType cbt)
{
	LOG_INF("Received stop publish media response");

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Stop publish timeout", cbt);
			P2PStopPublishReqData req_data;
			if (ParseRequest(msg, req_data)) {
				m_call_handler->OnStopPublishResult(req_data.media, false);
			}
			return;
		}

		int code;
		P2PStopPublishResData res_data;
		if (!ParseResponse(msg, res_data, code)) {
			return;
		}

		auto iter = m_pub_medias.find(res_data.media.media_id);
		if (iter == m_pub_medias.end()) {
			LOG_ERR("Media with ID {} is not being published", res_data.media.media_id);
			return;
		}

		if (code != 0) {
			LOG_ERR("Stop publish response error");
			m_call_handler->OnStopPublishResult(res_data.media, false);
			return;
		}

		// 通过 media_id 查找 transceiver
		auto transceiver = FindTransceiverByKind(res_data.media.media_type);
		if (!transceiver) {
			LOG_ERR("Cannot find transceiver, media type: {}", res_data.media.media_type);
			return;
		}

		auto sender = transceiver->sender();
		if (!sender) {
			LOG_ERR("Invalid sender");
			return;
		}

		if (!sender->track()) {
			LOG_WRN("Sender dit not set track");
		}

		sender->SetTrack(nullptr);
		transceiver->SetDirection(webrtc::RtpTransceiverDirection::kRecvOnly);

		m_pub_medias.erase(iter);

		m_call_handler->OnStopPublishResult(res_data.media, true);

		StartNegotiationAsOfferer();

		LOG_INF("Stopped publishing media: {}", ToString(res_data.media));
	});
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStopPublishNotify(const Message& msg, CBType cbt)
{
	LOG_INF("Received stop publish media notify");

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (msg.data.is_null()) {
			LOG_ERR("Stop publish media notify data is null");
			return;
		}

		P2PStopPublishNotifyData notify_data;
		try {
			notify_data = msg.data.get<P2PStopPublishNotifyData>();
		} catch (const std::exception& e) {
			LOG_ERR("Failed to parse stop publish media notify data: {}", e.what());
			return;
		}

		if (notify_data.call_id != m_call_id) {
			LOG_ERR("Stop publish media notify call ID does not match current call ID: {} != {}",
				notify_data.call_id, m_call_id);
			return;
		}

		if (notify_data.user_id.empty()) {
			LOG_ERR("Stop publish media notify missing user ID");
			return;
		}

		auto iter = m_sub_medias.find(notify_data.media.media_id);
		if (iter == m_sub_medias.end()) {
			LOG_ERR("Media with ID {} is not being published by user {}", 
				notify_data.media.media_id, notify_data.user_id);
			return;
		}
		m_sub_medias.erase(iter);

		if (m_call_handler) {
			m_call_handler->OnStopPublish(notify_data.user_id, notify_data.media);
		}
		
		m_dispatcher->SendMsg(MakeP2PStopPublishAckMsg(msg.rid, 0, "ok", notify_data.call_id,
			notify_data.user_id, notify_data.media));
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStartSubscribeRes(const Message& msg, CBType cbt)
{
	LOG_INF("Received start subscribe media response");

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Start subscribe timeout", cbt);
			P2PStartSubscribeReqData req_data;
			if (ParseRequest(msg, req_data)) {
				m_call_handler->OnStartSubscribeResult(req_data.pub_user_id, req_data.media, false);
			}
			return;
		}

		int code;
		P2PStartSubscribeResData res_data;
		if (!ParseResponse(msg, res_data, code)) {
			return;
		}

		if (code != 0) {
			LOG_ERR("Start subscribe response error");
			m_call_handler->OnStartSubscribeResult(res_data.pub_user_id, res_data.media, false);
			return;
		}

		auto iter = m_sub_medias.find(res_data.media.media_id);
		if (iter == m_sub_medias.end()) {
			LOG_ERR("Media with ID {} is not being published", res_data.media.media_id);
			return;
		}

		m_sub_medias.insert({ res_data.media.media_id, {res_data.pub_user_id, res_data.media} });

		m_call_handler->OnStartSubscribeResult(res_data.pub_user_id, res_data.media, true);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStopSubscribeRes(const Message& msg, CBType cbt)
{
	LOG_INF("Received stop subscribe media response");

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Stop subscribe timeout", cbt);
			P2PStopSubscribeReqData req_data;
			if (ParseRequest(msg, req_data)) {
				m_call_handler->OnStopSubscribeResult(req_data.pub_user_id, req_data.media, false);
			}
			return;
		}

		int code;
		P2PStopSubscribeResData res_data;
		if (!ParseResponse(msg, res_data, code)) {
			return;
		}

		auto iter = m_sub_medias.find(res_data.media.media_id);
		if (iter == m_sub_medias.end()) {
			LOG_ERR("Media with ID {} is not being published", res_data.media.media_id);
			return;
		}

		if (code != 0) {
			LOG_ERR("Stop subscribe response error");
			m_call_handler->OnStopSubscribeResult(res_data.pub_user_id, res_data.media, false);
			return;
		}

		m_sub_medias.erase(iter);
		m_call_handler->OnStopSubscribeResult(res_data.pub_user_id, res_data.media, true);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnStartProduceNotify(const Message& msg, CBType cbt)
{
	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg]() {
		P2PStartProduceNotifyData notify_data;
		if (!ParseRequest(msg, notify_data)) {
			return;
		}

		m_dispatcher->SendMsg(MakeP2PStartProduceAckMsg(msg.rid, 0, "ok", m_call_id, GetUserId()));

		StartProducing(notify_data.media);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
P2PCallAgent::MediaStreamTrackSP P2PCallAgent::CreateTrackFromMedia(const MediaInfo& media)
{

	// 当前只处理设备类型媒体
	if (media.src_type != "device") {
		LOG_ERR("Unsupported media source type: {}", media.src_type);
		return nullptr;
	}

	if (media.src_id.empty()) {
		LOG_ERR("Video source ID is empty");
		return nullptr;
	}

	auto it = m_pub_medias.find(media.media_id);
	if (it == m_pub_medias.end()) {
		LOG_ERR("Cannot find publish media");
		return nullptr;
	}

	if (media.media_type == "audio") {
		LOG_INF("Creating audio track with default device.");

		auto audio_source = m_pc_factory->CreateAudioSource(cricket::AudioOptions());
		if (!audio_source) {
			LOG_ERR("Failed to create audio source for device: {}", media.src_id);
			return nullptr;
		}

		it->second.audio_source = audio_source;

		auto audio_track = m_pc_factory->CreateAudioTrack(media.media_id, audio_source.get());
		if (!audio_track) {
			LOG_ERR("Create audio track failed");
			return nullptr;
		}

		return audio_track;
	} else if (media.media_type == "video") {
		LOG_INF("Creating video track with device_id: {}", media.src_id);		

		auto video_source = CapturerTrackSource::Create(640, 480, 30, media.src_id);
		if (!video_source) {
			LOG_ERR("Failed to create video track source for device: {}", media.src_id);
			return nullptr;
		}

		it->second.video_source = video_source;

		auto video_track = m_pc_factory->CreateVideoTrack(video_source, media.media_id);
		if (!video_track) {
			LOG_ERR("Create video track failed");
			return nullptr;
		}

		return video_track;
	}
	else {
		LOG_ERR("Unsupported media type: {}", media.media_type);
		return nullptr;
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PCallAgent::CreateDefaultTransceivers()
{
	webrtc::RtpTransceiverInit audio_init;
	audio_init.direction = webrtc::RtpTransceiverDirection::kRecvOnly;
	auto audio_result = m_pc_wrapper->AddTransceiver(
		cricket::MediaType::MEDIA_TYPE_AUDIO, audio_init);
	if (!audio_result.ok()) {
		LOG_ERR("Failed to add audio transceiver: {}", audio_result.error().message());
		return false;
	}

	LOG_INF("Added init audio transceiver");

	webrtc::RtpTransceiverInit video_init;
	video_init.direction = webrtc::RtpTransceiverDirection::kRecvOnly;
	auto video_result = m_pc_wrapper->AddTransceiver(
		cricket::MediaType::MEDIA_TYPE_VIDEO, video_init);
	if (!video_result.ok()) {
		LOG_ERR("Failed to add video transceiver: {}", video_result.error().message());
		return false;
	}

	LOG_INF("Added init video transceiver");

	return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PCallAgent::CreatePeerConnection()
{
	RTC_DCHECK(m_sdk_thread->IsCurrent());

	if (m_pc_wrapper) {
		LOG_WRN("An existing PeerConnectionWrapper was found, closing it before creating a new one.");
		ClosePeerConnection();
	}

	auto self = std::static_pointer_cast<P2PCallAgent>(shared_from_this());

	m_pc_wrapper = std::make_unique<PeerConnectionWrapper>(m_sdk_thread, self);
	if (!m_pc_wrapper->Init(m_pc_factory.get())) {
		LOG_ERR("Failed to initialize PeerConnectionWrapper.");
		m_pc_wrapper = nullptr; // 创建失败，清理指针
		return false;
	}

	LOG_INF("PeerConnectionWrapper created and initialized successfully.");

	return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::ClosePeerConnection()
{
	if (m_pc_wrapper) {
		m_pc_wrapper = nullptr; // RefCounted 指针置空即可，它会自动 Close 和销毁
	}
	m_neg_state = NegotiationState::kIdle;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PCallAgent::FindSubMediaByTrackId(const std::string& track_id, SubMedia& media)
{
	for (const auto& item : m_sub_medias) {
		if (item.second.track_id == track_id) {
			media = item.second;
			return true;
		}
	}

	return false;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PCallAgent::FindSubMediaByMediaId(const std::string& media_id, SubMedia& media)
{
	auto it = m_sub_medias.find(media_id);
	if (it != m_sub_medias.end()) {
		media = it->second;
		return true;
	}

	LOG_WRN("Media with media ID {} not found", media_id);

	return false;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::UpdateSubMediaTrackId(const std::string& msid, const std::string& track_id)
{
	auto it = m_sub_medias.find(msid);
	if (it != m_sub_medias.end()) {
		it->second.track_id = track_id;
	}
	else {
		LOG_WRN("Cannot find subscribe media: {}", msid);
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnAddTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
	const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>& streams)
{
	auto track = receiver->track();
	if (!track) {
		LOG_ERR("Track is null in OnAddTrack callback.");
		return;
	}

	if (streams.empty()) {
		LOG_ERR("No streams associated with the track in OnAddTrack callback.");
		return;
	}

	LOG_INF("OnAddTrack, track ID: {}, kind: {}, streams: {}", track->id(), track->kind(), 
		streams.size());

	auto media_stream_id = streams[0]->id();

	SubMedia media;
	if (!FindSubMediaByMediaId(media_stream_id, media)) {
		LOG_ERR("Failed to find remote media by media stream ID: {}", media_stream_id);
		return;
	}

	UpdateSubMediaTrackId(media_stream_id, track->id());

	m_call_handler->OnAddTrack(media.user_id, media.media, track);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnRemoveTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver)
{
	LOG_DBG("OnRemoveTrack: {}", receiver->id());

	auto track = receiver->track();
	if (!track) {
		LOG_ERR("Track is null in OnRemoveTrack callback.");
		return;
	}

	LOG_INF("OnRemoveTrack, track ID:{}, kind:{}", track->id(), track->kind());

	SubMedia media;
	if (!FindSubMediaByTrackId(track->id(), media)) {
		LOG_WRN("Failed to find remote media by track ID: {}", track->id());
	}
	else {
		m_call_handler->OnRemoveTrack(media.user_id, media.media);
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnIceCandidate(const webrtc::IceCandidateInterface* candidate)
{
	LOG_DBG("New ICE candidate");

	std::string sdp_mid = candidate->sdp_mid();
	int sdp_mline_index = candidate->sdp_mline_index();
	std::string sdp;
	if (!candidate->ToString(&sdp)) {
		LOG_ERR("Failed to serialize candidate in OnIceCandidate callback");
		return;
	}

	if (!m_dispatcher->SendMsg(MakeP2PCandidateNotifyMsg(
		m_call_id,
		GetUserId(),
		sdp_mid,
		sdp_mline_index,
		sdp))) {
		LOG_ERR("Failed to send candidate notify");
		return;
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnIceConnectionChange(
	webrtc::PeerConnectionInterface::IceConnectionState new_state) 
{

	switch (new_state) {
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionNew:
			LOG_INF("ICE connection state is new");
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionChecking:
			LOG_INF("ICE connection state is checking");
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionConnected:
			LOG_INF("ICE connection state is connected");
			m_call_state = CallState::Connected;
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionCompleted:
			LOG_INF("ICE connection state is completed");
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionFailed:
			LOG_ERR("ICE connection state failed");
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionDisconnected:
			LOG_INF("ICE connection state is disconnected");
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionClosed:
			LOG_INF("ICE connection state is closed");
			break;
		case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionMax:
			LOG_INF("ICE connection state is max");
			break;
		default:
			LOG_ERR("Unknown ICE connection state: {}", new_state);
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state)
{

	switch (new_state) {
		case webrtc::PeerConnectionInterface::SignalingState::kStable:
			LOG_INF("Signaling state is stable");
			break;
		case webrtc::PeerConnectionInterface::SignalingState::kHaveLocalOffer:
			LOG_INF("Signaling state is have local offer");
			break;
		case webrtc::PeerConnectionInterface::SignalingState::kHaveLocalPrAnswer:
			LOG_INF("Signaling state is have local pranswer");
			break;
		case webrtc::PeerConnectionInterface::SignalingState::kHaveRemotePrAnswer:
			LOG_INF("Signaling state is have remote pranswer");
			break;
		case webrtc::PeerConnectionInterface::SignalingState::kHaveRemoteOffer:
			LOG_INF("Signaling state is have remote offer");
			break;
		case webrtc::PeerConnectionInterface::SignalingState::kClosed:
			LOG_INF("Signaling state is closed");
			break;
		default:
			LOG_INF("Other signaling state: {}", new_state);
			break;
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnRenegotiationNeeded()
{
	if (m_is_negotiating) {
		LOG_INF("Renegotiation already in progress");
		return;
	}

	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this]() {
		LOG_INF("Renegotiation needed");
		if (m_neg_state == NegotiationState::kStable) {
			StartNegotiationAsOfferer();
		}
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::OnIceGatheringChange(
	webrtc::PeerConnectionInterface::IceGatheringState new_state)
{

	switch (new_state) {
		case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew:
			LOG_INF("ICE gathering state is new");
			break;
		case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringGathering:
			LOG_INF("ICE gathering state is gathering");
			break;
		case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringComplete:
			LOG_INF("ICE gathering state is complete");
			break;
		default:
			LOG_ERR("Unknown ICE gathering state: {}", new_state);
	}
}

//--------------------------------------------------------------------------------------------------
// 轨道队列管理方法实现
//--------------------------------------------------------------------------------------------------
void P2PCallAgent::StartProducing(const MediaInfo& media)
{
	auto iter = m_pub_medias.find(media.media_id);
	if (iter == m_pub_medias.end()) {
		LOG_ERR("Media with ID {} is not found in local publish medias", media.media_id);
		return;
	}

	auto track = CreateTrackFromMedia(media);
	if (!track) {
		LOG_ERR("Failed to create track from media info");
		return;
	}

	// 通过 media_type 找到 transceiver
	auto transceiver = FindTransceiverByKind(media.media_type);
	if (!transceiver) {
		LOG_ERR("Cannot find transceiver, media type: {}", media.media_type);
		return;
	}

	auto sender = transceiver->sender();
	if (!sender) {
		LOG_ERR("Invalid sender");
		return;
	}

	if (sender->track()) {
		LOG_WRN("Sender already has track, removing it first");
		sender->SetTrack(nullptr);
	}

	if (!sender->SetTrack(track.get())) {
		LOG_ERR("Set track failed");
		return;
	}

	sender->SetStreams({media.media_id});

	transceiver->SetDirection(webrtc::RtpTransceiverDirection::kSendRecv);

	StartNegotiationAsOfferer();
}

//--------------------------------------------------------------------------------------------------
// 轨道队列管理方法实现
//--------------------------------------------------------------------------------------------------
P2PCallAgent::RtpTransceiverSP P2PCallAgent::FindTransceiverByKind(const std::string& media_type)
{
    if (!m_pc_wrapper || !m_pc_wrapper->pc()) {
        return nullptr;
    }

	cricket::MediaType kind = (media_type == "audio") ?
		cricket::MediaType::MEDIA_TYPE_AUDIO : cricket::MediaType::MEDIA_TYPE_VIDEO;

    auto transceivers = m_pc_wrapper->pc()->GetTransceivers();
    for (const auto& transceiver : transceivers) {
        if (transceiver->receiver()->media_type() == kind) {
            return transceiver;
        }
    }
    return nullptr;
}

} // namespace p2pms
